type KeyState = {
    [key: string]: boolean;
};

export type InputFrame = {
    timestamp: number;
    keys: KeyState;
    mouse: {
        x: number;
        y: number;
        buttons: boolean[];
    };
};

export enum KeyCode{
    KeyA = "KeyA",
    KeyW = "KeyW",
    KeyD = "KeyD",
    KeyS = "KeyS",
}

export class Input {
    private static keys: KeyState = {};
    private static mouse = {
        x: 0,
        y: 0,
        buttons: [false, false, false]
    };
    private static inputHistory: InputFrame[] = [];
    private static maxHistory = 60; // Store up to 1 second of input at 60fps

    constructor(private canvas: HTMLCanvasElement) {
        this.setupEventListeners();
    }

    private setupEventListeners(): void {
        window.addEventListener('keydown', (e) => this.onKeyDown(e));
        window.addEventListener('keyup', (e) => this.onKeyUp(e));
        this.canvas.addEventListener('mousemove', (e) => this.onMouseMove(e));
        this.canvas.addEventListener('mousedown', (e) => this.onMouseDown(e));
        this.canvas.addEventListener('mouseup', (e) => this.onMouseUp(e));
    }

    private onKeyDown(e: KeyboardEvent): void {
        Input.keys[e.code] = true;
    }

    private onKeyUp(e: KeyboardEvent): void {
        Input.keys[e.code] = false;
        this.recordInputFrame();
    }

    private onMouseMove(e: MouseEvent): void {
        const rect = this.canvas.getBoundingClientRect();
        Input.mouse.x = e.clientX - rect.left;
        Input.mouse.y = e.clientY - rect.top;
        this.recordInputFrame();
    }

    private onMouseDown(e: MouseEvent): void {
        if (e.button >= 0 && e.button < 3) {
            Input.mouse.buttons[e.button] = true;
        }
    }

    private onMouseUp(e: MouseEvent): void {
        if (e.button >= 0 && e.button < 3) {
            Input.mouse.buttons[e.button] = false;
        }
    }

    public static isKeyDown(code: string): boolean {
        return !!Input.keys[code];
    }

    public static getMousePosition(): { x: number; y: number } {
        return { x: Input.mouse.x, y: Input.mouse.y };
    }

    public static isMouseButtonDown(button: number): boolean {
        return !!Input.mouse.buttons[button];
    }

    private recordInputFrame(): void {
        const frame: InputFrame = {
            timestamp: performance.now(),
            keys: {...Input.keys},
            mouse: {
                x: Input.mouse.x,
                y: Input.mouse.y,
                buttons: [...Input.mouse.buttons]
            }
        };
        Input.inputHistory.push(frame);
        
        // Maintain history size
        if (Input.inputHistory.length > Input.maxHistory) {
            Input.inputHistory.shift();
        }
    }

    public static getInputFramesSince(timestamp: number): InputFrame[] {
        return Input.inputHistory.filter(frame => frame.timestamp >= timestamp);
    }

    public static getLatestInputFrame(): InputFrame | null {
        return Input.inputHistory.length > 0 
            ? Input.inputHistory[Input.inputHistory.length - 1]
            : null;
    }

    destroy(): void {
        window.removeEventListener('keydown', this.onKeyDown);
        window.removeEventListener('keyup', this.onKeyUp);
        this.canvas.removeEventListener('mousemove', this.onMouseMove);
        this.canvas.removeEventListener('mousedown', this.onMouseDown);
        this.canvas.removeEventListener('mouseup', this.onMouseUp);
    }
}
